/**
 * Baijiahulian.com Inc. Copyright (c) 2014-2014 All Rights Reserved.
 */
package com.baijia.jigou.util;

import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeoutException;

import org.apache.commons.lang.builder.EqualsBuilder;
import org.apache.commons.lang.builder.HashCodeBuilder;
import org.apache.commons.lang.builder.ToStringBuilder;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.baijia.jigou.po.OptObj;
import com.baijia.jigou.po.OrgAccount;
import com.baijia.jigou.po.OrgCascadeAccount;

import net.rubyeye.xmemcached.exception.MemcachedException;

/**
 * @title WebContext
 * @desc 存储一次请求中得上下文信息
 * @author taoyaping
 * @date 2014年10月17日
 * @version 1.0
 */
public class WebContext {

    public static final Logger logger = LoggerFactory.getLogger(WebContext.class);

    private static final String HTTP_SESSION_ID = "HTTP_SESSION_ID";
    private static final String HTTP_SESSION_INSTANCE = "HTTP_SESSION_INSTANCE";
    private static final String LOGIN_USER_KEY = "LOGIN_USER_KEY";
    private static final String CASCADE_LOGIN_USER_KEY = "CASCADE_LOGIN_USER_KEY";
    private static final String OPT_OBJ_KEY = "OPT_OBJ_KEY";
    private static final int SESSION_EXP_TIME = 24 * 60 * 60;
    private static final ThreadLocal<Map<String, Object>> cache = new ThreadLocal<Map<String, Object>>();

    public static void init(String sessionId) {
        clear();
        Map<String, Object> map = new HashMap<String, Object>();
        cache.set(map);
        setSessionId(sessionId);
        WebSession session = getSessionFromMemcached(false);
        map.put(HTTP_SESSION_INSTANCE, session);
        logger.debug("session[{}]", getSession());
    }

    public static void clear() {
        clearSession();
        cache.remove();
    }

    public static <T> void set(String key, T value) {
        cache.get().put(key, value);
    }

    @SuppressWarnings("unchecked")
    public static <T> T get(String key, Class<T> clazz) {
        return (T) cache.get().get(key);
    }

    /**
     * @return HttpSession ID
     */
    public static String getSessionId() {
        String sessionId = (String) cache.get().get(HTTP_SESSION_ID);
        if (StringUtils.isBlank(sessionId)) {
            throw new RuntimeException("sessionid should not be null...");
        }

        return sessionId;
    }

    /**
     * 应该在初始化时被设置好
     * 
     * @param sessionId
     */
    private static void setSessionId(String sessionId) {
        cache.get().put(HTTP_SESSION_ID, sessionId);
    }

    /**
     * 这个方法应该只在登录和注册的时候被调用，其余入口应该调无参数那个
     * 
     * @param createIfAbsent
     * @return
     */
    @SuppressWarnings("rawtypes")
    public static final WebSession getSession(boolean createIfAbsent) {
        Map map = cache.get();
        if (map == null) {
            return null;
        }
        WebSession session = (WebSession) map.get(HTTP_SESSION_INSTANCE);
        if (null == session && createIfAbsent) {
            session = getSessionFromMemcached(createIfAbsent);
            if (session != null) {
                cache.get().put(HTTP_SESSION_INSTANCE, session);
            }
        }
        return session;
    }

    private static final WebSession getSessionFromMemcached(boolean createIfAbsent) {
        WebSession session = null;
        try {
            String key = getSessionMemcachedKey();
            session = (WebSession) OrgMemcachedUtil.get(key);
            logger.trace("read session from memcahced by key:{}-session:{}", key, session);
            // 如果没有,要求创建就创建
            if (null == session && createIfAbsent) {
                session = new WebSession(key);
                OrgMemcachedUtil.set(key, SESSION_EXP_TIME, session);
                logger.trace("trace new session...");
            }
            logger.debug("getSession-sessionKey:{}, session:{}", key, session);
        } catch (TimeoutException | InterruptedException | MemcachedException e) {
            logger.error("communicate with memcached error:{}", e);
        }

        return session;
    }

    /**
     * 获取登录的账号，未登录返回null 在filter里被保存
     * 
     * @return
     */
    public static OrgAccount getLoginAccount() {
        if (getSession() != null) {
            return getSession().getLoginAccount();
        }
        return null;
    }
    /**
     * 子帐号
     * 
     * @return
     */
    public static OrgCascadeAccount getCascadeLoginAccount() {
        if (getSession() != null) {
            return getSession().getCascadeLoginAccount();
        }
        return null;
    }

    public static final WebSession getSession() {
        return getSession(false);
    }

    public static void synSession() {
        if (getSession() != null) {
            getSession().flush();
        }
    }

    public static void destroySession() {
        if (getSession() != null) {
            getSession().setDestroy(true);
        }
    }
    
    public static void destroySession(String sessionId) {
        String jigouSessionId = "JIGOU_" + sessionId;
        try {
            OrgMemcachedUtil.delete(jigouSessionId);
        } catch (TimeoutException | InterruptedException | MemcachedException e) {
            e.printStackTrace();
        }
    }

    public static void clearSession() {
        if (getSession() != null) {
            getSession().clear();
        }
    }

    private static String getSessionMemcachedKey() {
        return "JIGOU_" + getSessionId();
    }

    /**
     * 判断当前用户是否已登录
     * 
     * @return
     */
    public static boolean isLogined() {
        return null != getSession();
    }

    public static final class WebSession implements Serializable {

        private static final long serialVersionUID = -2683121849488540792L;

        private final Map<String, Object> map = new HashMap<String, Object>();

        private String sessionKey;

        private boolean isDestroy = false;

        WebSession(String sessionKey) {
            this.sessionKey = sessionKey;
        }

        public Object getAttribute(String key) {
            return map.get(key);
        }

        public void setAttribute(String key, Object value) {
            map.put(key, value);
            this.flush();
        }

        public boolean isDestroy() {
            return isDestroy;
        }

        public void setDestroy(boolean isDestroy) {
            this.isDestroy = isDestroy;
            try {
                OrgMemcachedUtil.delete(sessionKey);
            } catch (TimeoutException | InterruptedException | MemcachedException e) {
                e.printStackTrace();
            }
        }

        public void clear() {
            map.clear();
        }

        public void setLoginAccount(OrgAccount org) {
            setAttribute(LOGIN_USER_KEY, org);
        }

        public OrgAccount getLoginAccount() {
            return (OrgAccount) getAttribute(LOGIN_USER_KEY);
        }
        
        public void setCascadeLoginAccount(OrgCascadeAccount org) {
            setAttribute(CASCADE_LOGIN_USER_KEY, org);
        }
        
        public OrgCascadeAccount getCascadeLoginAccount() {
            return (OrgCascadeAccount) getAttribute(CASCADE_LOGIN_USER_KEY);
        }

        public void setOptObj(OptObj obj) {
            setAttribute(OPT_OBJ_KEY, obj);
        }

        public OptObj getOptObj() {
            return (OptObj) getAttribute(OPT_OBJ_KEY);
        }

        public void flush() {
            try {
                if (isDestroy) {
                    OrgMemcachedUtil.delete(sessionKey);
                } else {
                    OrgMemcachedUtil.set(sessionKey, SESSION_EXP_TIME, this);
                    logger.trace("flush session-key:{}, value:{} to memcached", sessionKey, this);
                }
            } catch (TimeoutException | InterruptedException | MemcachedException e) {
                logger.error("flush sessionKey:{} to memecached error:{}", sessionKey, e);
            }
        }

        @Override
        public String toString() {
            return ToStringBuilder.reflectionToString(this);
        }

        @Override
        public int hashCode() {
            return HashCodeBuilder.reflectionHashCode(this);
        }

        @Override
        public boolean equals(Object obj) {
            return EqualsBuilder.reflectionEquals(this, obj);
        }
    }
}
